/**
 * @author Nicolas Lusa (lusan@usi.ch)
 * @author Isabella Nake (isabella.nake@usi.ch)
 */
package ch.usi.inf.pf2.gui.tool;

import java.awt.event.MouseEvent;

import ch.usi.inf.pf2.circuit.Component;
import ch.usi.inf.pf2.circuit.Wire;
import ch.usi.inf.pf2.circuit.Gate;
import ch.usi.inf.pf2.circuit.Input;
import ch.usi.inf.pf2.circuit.Output;
import ch.usi.inf.pf2.gui.Model;

public final class WireCreationTool extends Tool {

    private Wire connectionUnderConstruction;
    private boolean inputPin1;
    private boolean inputPin2;
    private boolean outputPin;
    private boolean validWire;
    private Component shape1;
    
    
    public WireCreationTool(final Model model) {
        super(model, "Select this tool and drag it from a component to another one to create a connection.", "../icons/wire.png", "../icons/wireBg.png");
        inputPin1 = false;
        inputPin2 = false;
        outputPin = false;
        validWire = true;
        shape1 = null;
    }
    
    public void mousePressed(final MouseEvent ev) {
    	connectionUnderConstruction = null;
        shape1 = getCircuit().findShapeAt(ev.getX(), ev.getY());
        if (shape1!=null) {
        	if(!(shape1 instanceof Wire)) {	
	        	if(shape1 instanceof Gate) {
	        		buildWireFromGate(ev);
	        	} else {
	                connectionUnderConstruction = new Wire(shape1.getOriginX(), shape1.getOriginY(), ev.getX(), ev.getY());
	                if(shape1 instanceof Input) {
	                	setTheStateOfTheOriginShapePins(false, false, true);
	                } else if(shape1 instanceof Output && ((Output) shape1).getInputPin() == null) 	{
	                	setTheStateOfTheOriginShapePins(true, false, false);
	                } else {
	                	connectionUnderConstruction = null;
	                }
	        	}
	            if(connectionUnderConstruction != null) {
	            	getCircuit().add(connectionUnderConstruction);
	            }
        	}	
        } else {
            connectionUnderConstruction = null;
        }
    }

	private void buildWireFromGate(final MouseEvent ev) {
		Gate gate = (Gate) shape1;
		if(firstClickInLeftPartOfShape(ev)) {
			if(firstClickInUpperLeftPartOfShape(ev)) {
				connectionUnderConstruction = new Wire(gate.getXInputPin1(), gate.getYInputPin1(), ev.getX(), ev.getY());
				setTheStateOfTheOriginShapePins(true, false, false);
			} else if(firstClickInLowerLeftPartOfShape(ev)){
				connectionUnderConstruction = new Wire(gate.getXInputPin2(), gate.getYInputPin2(), ev.getX(), ev.getY());
				setTheStateOfTheOriginShapePins(false, true, false);
			} else {
				connectionUnderConstruction = null;
			}
		} else {
			connectionUnderConstruction = new Wire(gate.getXOutputPin(), gate.getYOutputPin(), ev.getX(), ev.getY());
			setTheStateOfTheOriginShapePins(false, false, true);
		}
	}

	private boolean firstClickInLowerLeftPartOfShape(final MouseEvent ev) {
		return ev.getY() > shape1.getOriginY()  && ((Gate) shape1).getInputPin2() == null;
	}

	private boolean firstClickInUpperLeftPartOfShape(final MouseEvent ev) {
		return ev.getY() < shape1.getOriginY()  && ((Gate) shape1).getInputPin1() == null;
	}

	private boolean firstClickInLeftPartOfShape(final MouseEvent ev) {
		return ev.getX() < shape1.getOriginX();
	}

	private void setTheStateOfTheOriginShapePins(boolean inputPin1, boolean inputPin2, boolean outputPin) {
		this.inputPin1 = inputPin1;
		this.inputPin2 = inputPin2;
		this.outputPin = outputPin;
	}

    public void mouseDragged(final MouseEvent ev) {
        if (connectionUnderConstruction!=null) {
        	getCircuit().remove(connectionUnderConstruction);
        	connectionUnderConstruction.setX1(connectionUnderConstruction.getAnchorX());
        	connectionUnderConstruction.setY1(connectionUnderConstruction.getAnchorY());
        	connectionUnderConstruction.setX2(ev.getX());
        	connectionUnderConstruction.setY2(ev.getY());
        	getCircuit().add(connectionUnderConstruction);
        	
        }
    }

    public void mouseReleased(final MouseEvent ev) {
        validWire = true;
        if (connectionUnderConstruction!=null) {
            getCircuit().remove(connectionUnderConstruction);
            final Component shape2 = getCircuit().findShapeAt(ev.getX(), ev.getY());
            if (shape2!=null) {
            	if(shape2 instanceof Gate) {
            		if(ev.getX() < shape2.getOriginX() && outputPin) {
            			connectWireWithInputPinOfGate(ev, shape2);
            		} else if(ev.getX() > shape2.getOriginX() && (inputPin1 || inputPin2)){
            			connectWireWithOutputPinOfGate(shape2);
            		} else {
            			validWire = false;
            		}
            	} else if(shape2 instanceof Input && (inputPin1 || inputPin2)) 	{
            		connectWireWithInput(shape2);
                } else if(shape2 instanceof Output && outputPin && ((Output) shape2).getInputPin() == null) 	{
                	connectWireWithOutput(shape2);
                } else {
                	validWire = false;
                }
            	if(validWire) {
            		setWireInTheOriginShape();
            		
	            	getCircuit().add(connectionUnderConstruction);
	                getSelection().clear();
	                getSelection().add(connectionUnderConstruction);
            	}
            }
        }
    }

	private void setWireInTheOriginShape() {
		if(shape1 instanceof Gate) {
			if(inputPin1) {
				((Gate) shape1).setWireInputPin1(connectionUnderConstruction);
			} else if(inputPin2) {
				((Gate) shape1).setWireInputPin2(connectionUnderConstruction);
			} else {
				((Gate) shape1).setWireOutputPin(connectionUnderConstruction);
			}
		} else if(shape1 instanceof Input) {
			((Input) shape1).setWire(connectionUnderConstruction);
		} else if(shape1 instanceof Output) {
			((Output) shape1).setWire(connectionUnderConstruction);
		}
	}

	private void connectWireWithOutput(final Component shape2) {
		connectionUnderConstruction.setX1(connectionUnderConstruction.getAnchorX());
		connectionUnderConstruction.setY1(connectionUnderConstruction.getAnchorY());
		connectionUnderConstruction.setX2(((Output) shape2).getOriginX());
		connectionUnderConstruction.setY2(((Output) shape2).getOriginY());
		((Output) shape2).setWire(connectionUnderConstruction);
	}

	private void connectWireWithInput(final Component shape2) {
		connectionUnderConstruction.setX2(connectionUnderConstruction.getAnchorX());
		connectionUnderConstruction.setY2(connectionUnderConstruction.getAnchorY());
		connectionUnderConstruction.setX1(((Input) shape2).getOriginX());
		connectionUnderConstruction.setY1(((Input) shape2).getOriginY());
		((Input) shape2).setWire(connectionUnderConstruction);
	}

	private void connectWireWithOutputPinOfGate(final Component shape2) {
		connectionUnderConstruction.setX2(connectionUnderConstruction.getAnchorX());
		connectionUnderConstruction.setY2(connectionUnderConstruction.getAnchorY());
		connectionUnderConstruction.setX1(((Gate) shape2).getXOutputPin());
		connectionUnderConstruction.setY1(((Gate) shape2).getYOutputPin());
		((Gate) shape2).setWireOutputPin(connectionUnderConstruction);
	}

	private void connectWireWithInputPinOfGate(final MouseEvent ev,
			final Component shape2) {
		connectionUnderConstruction.setX1(connectionUnderConstruction.getAnchorX());
		connectionUnderConstruction.setY1(connectionUnderConstruction.getAnchorY());
		if(ev.getY() < shape2.getOriginY() && ((Gate) shape2).getInputPin1() == null) {
			connectionUnderConstruction.setX2(((Gate) shape2).getXInputPin1());
			connectionUnderConstruction.setY2(((Gate) shape2).getYInputPin1());
			((Gate) shape2).setWireInputPin1(connectionUnderConstruction);
		} else if(ev.getY() > shape2.getOriginY() && ((Gate) shape2).getInputPin2() == null) {
			connectionUnderConstruction.setX2(((Gate) shape2).getXInputPin2());
			connectionUnderConstruction.setY2(((Gate) shape2).getYInputPin2());
			((Gate) shape2).setWireInputPin2(connectionUnderConstruction);
		}
		else {
			validWire = false;
		}
	}

}

